package com.xiaobias.a47;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Configuration
@SuppressWarnings("all")
public class A47_2 {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_2.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>> 1. 数组类型");
//        testArray(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>> 2. List 类型");
//        testList(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>> 3. applicationContext");
//        testApplicationContext(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>> 4. 范型");
//        testGeneric(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>> 5. @Qualifier");
        testQualifier(beanFactory);
    }

    private static void testQualifier(DefaultListableBeanFactory factory) throws Exception {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
        Class<?> type = dd1.getDependencyType();
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(factory);
        String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, type);
        for (String name : names) {
//            System.out.println(name);
            BeanDefinition bd = factory.getMergedBeanDefinition(name);
            //                                                              @Qualifier("service2")
            if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd1)) {
                System.out.println(name);
                System.out.println(dd1.resolveCandidate(name, type, factory));
            }
        }
    }

    private static void testGeneric(DefaultListableBeanFactory factory) throws NoSuchFieldException {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
        ResolvableType type = dd1.getResolvableType();
        String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, type);
        for (String name : names) {
            System.out.println(name);
        }
    }

    private static void testApplicationContext(DefaultListableBeanFactory factory) throws Exception {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);

        Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        resolvableDependencies.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(factory);
        // forEach 函数式接口，没办法返回结果
//        dependencies.forEach((k, v) -> {
//            System.out.println("key: " +  k + " value: " + v);
//        });
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            // 左边类型                 右边类型
            // 左边类型能否赋值给右边类型
            if (entry.getKey().isAssignableFrom(dd1.getDependencyType())) {
                System.out.println(entry.getValue());
                break;
            }
        }
    }

    private static void testList(DefaultListableBeanFactory factory) throws NoSuchFieldException {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
        if (dd1.getDependencyType() == List.class) {
            Class<?> resolve = dd1.getResolvableType().getGeneric().resolve();
            System.out.println(resolve);
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, resolve);
            List<Object> list = new ArrayList<>();
            for (String name : names) {
                Object bean = dd1.resolveCandidate(name, resolve, factory);
                list.add(bean);
            }
            System.out.println(list);
        }
    }

    private static void testArray(DefaultListableBeanFactory factory) throws NoSuchFieldException {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
        if (dd1.getDependencyType().isArray()) {
            Class<?> componentType = dd1.getDependencyType().getComponentType();
            System.out.println(componentType);
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, componentType);
            List<Object> beans = new ArrayList<>();
            for (String name : names) {
                System.out.println(name);
                Object bean = dd1.resolveCandidate(name, componentType, factory);
                beans.add(bean);
            }
            Object array = factory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
            System.out.println(array);
        }

    }

    static class Target {
        @Autowired
        private Service[] serviceArray;
        @Autowired
        private List<Service> serviceList;
        @Autowired
        private ConfigurableApplicationContext applicationContext;
        @Autowired
        private Dao<Teacher> dao;
        @Autowired
        @Qualifier("service2")
        private Service service;
    }

    interface Service {}
    @Component
    static class Service1 implements Service {}
    @Component
    static class Service2 implements Service {}
    @Component
    static class Service3 implements Service {}
    interface Dao<T> {}
    static class Student {}
    static class Teacher {}
    @Component("dao1")
    static class Dao1 implements Dao<Student> {}
    @Component("dao2")
    static class Dao2 implements Dao<Teacher> {}
}
